#include "imagemanager.h"

#include "image.h"

#include "il/il.h"

namespace dw {
	namespace graffiti {

void ImageManager::initialize()
{
    ilInit();

	ilOriginFunc(IL_ORIGIN_LOWER_LEFT);
    ilEnable(IL_ORIGIN_SET);

    ilEnable(IL_FILE_OVERWRITE);
}

void ImageManager::terminate()
{
	dwAssert(m_images.empty(), "Some images have not been released");

	m_images.clear();

	ilShutDown();
}

const Image* ImageManager::get(const std::string& filePath)
{
	ImagesMap::iterator foundIt = m_images.find(filePath);

	return foundIt != m_images.end() ? foundIt->second : NULL;
}

const Image* ImageManager::load(const std::string& filePath)
{
	const Image* image = get(filePath);

	if (image == NULL)
	{
		std::ifstream fstream(filePath.c_str(), std::ios::binary);
		
		if (fstream.is_open())
		{
			image = load(fstream);

			if (image != NULL)
			{
				m_images.insert(std::make_pair(filePath, image));
			}

			fstream.close();
		}
	}

	return image;
}

void ImageManager::unload(const Image* image)
{
	dwAssert(image != NULL, "Invalid parameter: image is null");

	ImagesMap::const_iterator imagesEnd = m_images.end();
	for (ImagesMap::iterator imageIt = m_images.begin(); imageIt != imagesEnd; ++imageIt)
	{
		if (imageIt->second == image)
		{
			delete imageIt->second;
			m_images.erase(imageIt);

			return ;
		}
	}
}

void ImageManager::save(const std::string& filePath, const Image* image)
{
}

Image* ImageManager::load(std::istream& is)
{
	ILuint id;
	char* imageData;
	dw::uint32 imageDataSize;
	
	is.seekg(0, std::ios::end);
	imageDataSize = is.tellg();
	is.seekg(0, std::ios::beg);
	imageData = new char [imageDataSize];
	is.read(imageData, imageDataSize);

	ilGenImages(1, &id);
	ilBindImage(id);
	ilSetInteger(IL_KEEP_DXTC_DATA, IL_TRUE);
	if (!ilLoadL(IL_TYPE_UNKNOWN, imageData, imageDataSize))
	{
		ilDeleteImages(1, &id);
		delete [] imageData;

		return NULL;
	}
	
	ILint ilCubeFlag = ilGetInteger(IL_IMAGE_CUBEFLAGS);
	ILint ilFormat = ilGetInteger(IL_IMAGE_FORMAT);
	ILint ilDataType = ilGetInteger(IL_IMAGE_TYPE);
	ILint ilDXTCFormat = ilGetInteger(IL_DXTC_DATA_FORMAT);

	glm::ivec3 size = glm::ivec3(ilGetInteger(IL_IMAGE_WIDTH), ilGetInteger(IL_IMAGE_HEIGHT), ilGetInteger(IL_IMAGE_DEPTH));
	ILint bpp = ilGetInteger(IL_IMAGE_BITS_PER_PIXEL);
	ILint mipmapsCount = ilGetInteger(IL_NUM_MIPMAPS);
	ILint dimension = ilCubeFlag ? image::dimension::cube : (size.y == 1) ? image::dimension::one : (size.z == 1) ? image::dimension::two : image::dimension::three;
	dw::uint32 numFaces = ilCubeFlag ? 6 : 1;

	pixel::format::formats pixelFormat;

	switch (ilFormat)
	{
	case IL_COLOR_INDEX : dwFail("Format is not handled"); break ;
	case IL_ALPHA : dwFail("Format is not handled"); break ;
	case IL_RGB : pixelFormat = pixel::format::rgb; break ;
	case IL_RGBA : pixelFormat = pixel::format::rgba; break ;
	case IL_BGR : pixelFormat = pixel::format::bgr; break ;
	case IL_BGRA : pixelFormat = pixel::format::bgra; break ;
	case IL_LUMINANCE : pixelFormat = pixel::format::luminance; break ;
	case IL_LUMINANCE_ALPHA : dwFail("Format is not handled"); break ;
	}

	if (ilDXTCFormat != IL_DXT_NO_COMP)
	{
		switch (ilDXTCFormat)
		{
			case IL_DXT1 : pixelFormat = pixel::format::dxt1; break ;
			case IL_DXT3 : pixelFormat = pixel::format::dxt3; break ;
			case IL_DXT5 : pixelFormat = pixel::format::dxt5; break ;
		}
	}

	Image* image = new Image(size, pixelFormat, bpp, mipmapsCount, numFaces > 1);

	for (dw::uint32 mipmap = 0; mipmap < (dw::uint32)(mipmapsCount + 1); ++mipmap)
	{
		ilActiveMipmap(mipmap);

		if (ilDXTCFormat != IL_DXT_NO_COMP)
		{
			ILuint ilDXTCSize = ilGetDXTCData(NULL, 0, ilDXTCFormat);
			
			image->resize(mipmap, ilDXTCSize);
			void* mipmapData = image->map(mipmap);

			ilGetDXTCData(mipmapData, ilGetDXTCData(NULL, 0, ilDXTCFormat), ilDXTCFormat);
		}
		else
		{
			image->set(mipmap, ilGetData(), ilGetInteger(IL_IMAGE_SIZE_OF_DATA));
		}
	}
	ilDeleteImages(1, &id);

	delete [] imageData;

	return image;
}

	} // namespace graffiti
} // namespace dw
